<?php
/**
 * Created by  青岛火一五信息科技有限公司
 * author: Zane
 * QQ  : 3186355915
 * Date: 2019/4/27
 * Time: 01:26
 */

namespace LaravelPatterns;


class Container
{
    protected $bindings = [];

    public function bind($abstract, $concrete=null, $shared=false)
    {
        if (!$concrete instanceof \Closure) {
            $concrete = $this->getClosure($abstract,$concrete);
        }
        $this->bindings[$abstract]=compact('concrete','shared');
   }

    protected function getClosure($abstract, $concrete)
    {
        return function ($c) use($abstract, $concrete) {
            $method = ($abstract==$concrete)? 'build':'make';
            return $c->$method($concrete);
        };
    }

    public function make($abstract)
    {
        $concrete=$this->getConcrete($abstract);
        if ($this->isBuildable($concrete,$abstract)) {
            $object = $this->build($concrete);
        } else {
            $object = $this->make($concrete);
        }

        return $object;
    }

    protected function getConcrete($abstract)
    {
        if (!isset($this->bindings[$abstract])) {
            return $abstract;
        }
        return $this->bindings[$abstract]['concrete'];
    }

    protected function isBuildable($concrete, $abstract)
    {
        return $concrete===$abstract || $concrete instanceof \Closure;
    }


    public function build($concrete)
    {
        if ($concrete instanceof \Closure) {
            return $concrete($this);
        }

        try{
            $reflector = new \ReflectionClass($concrete);
            if (!$reflector->isInstantiable()) {
                echo $message = "Target [$concrete] is not instantiable.";
                die;
            }
            $constructor = $reflector->getConstructor();
            if (is_null($constructor)){
                return new $concrete;
            }
            $dependencies = $constructor->getParameters();
            $instances = $this->getDependencies($dependencies);
            return $reflector->newInstanceArgs($instances);


        } catch (\Exception $exception) {
            echo $exception->getMessage();
            die;
        }





    }

    protected function getDependencies(array $parameters)
    {
        $dependencies = [];
        foreach ($parameters as $parameter) {
            $dependency = $parameter->getClass();
            if (is_null($dependency)){
                $dependencies[]=null;
            } else {
                $dependencies[]=$this->resolveClass($parameter);
            }
        }

        return (array) $dependencies;
    }

    protected function resolveClass(\ReflectionParameter $parameter)
    {
        return $this->make($parameter->getClass()->name);
    }
}